#include "stdlib.h"
#include "platform.h"
#include "core.h"
#include "keyboard.h"
#include "conf.h"
#include "camera.h"
#include "font.h"
#include "lang.h"
#include "gui.h"
#include "gui_lang.h"
#include "gui_draw.h"
#include "gui_menu.h"
#include "gui_palette.h"
#include "gui_fselect.h"
#include "gui_mbox.h"
#include "gui_mpopup.h"
#include "console.h"
#include "gui_batt.h"
#include "gui_osd.h"
#include "histogram.h"


#define SPLASH_TIME               20

// forward declarations
//-------------------------------------------------------------------
extern void dump_memory();
// both from platform/generic/shooting.c

static void gui_draw_osd();

static void gui_draw_splash(char* logo, int logo_size);

// Menu procs
//-------------------------------------------------------------------
static void gui_show_build_info(int arg);
static void gui_show_memory_info(int arg);
static void gui_draw_palette(int arg);

static void gui_draw_osd_le(int arg);

static void gui_draw_load_menu_rbf(int arg);
static void gui_draw_load_symbol_rbf(int arg);      //AKA

static void gui_draw_load_lang(int arg);
static void gui_menuproc_mkbootdisk(int arg);

static void gui_menuproc_reset(int arg);

static void gui_menuproc_swap_partitions(int arg);
static void gui_menuproc_reset_files(int arg);

static const char* gui_histo_show_enum(int change, int arg);
static const char* gui_histo_mode_enum(int change, int arg);
static const char* gui_histo_layout_enum(int change, int arg);
static const char* gui_font_enum(int change, int arg);


static const char* gui_show_values_enum(int change, int arg);
#if CAM_ADJUSTABLE_ALT_BUTTON
    static const char* gui_alt_mode_button_enum(int change, int arg);
#endif
static const char* gui_alt_power_enum(int change, int arg);
static const char* gui_fast_ev_step(int change, int arg);

static const char* gui_nd_filter_state_enum(int change, int arg);
//static const char* gui_tv_enum(int change, int arg);
static const char* gui_hide_osd_enum(int change, int arg);
static const char* gui_clock_format_enum(int change, int arg);
static const char* gui_clock_indicator_enum(int change, int arg);
void rinit();

// Menu callbacks
//-------------------------------------------------------------------
static void cb_step_25();
static void cb_battery_menu_change(unsigned int item);


// for memory info, duplicated from lowlevel
extern const char _start,_end;


// Menu definition
//-------------------------------------------------------------------




static CMenuItem misc_submenu_items[] = {

#if CAM_SWIVEL_SCREEN
    {0x28,LANG_MENU_MISC_FLASHLIGHT,            MENUITEM_BOOL,          &conf.flashlight },
#endif
    {0x5c,LANG_MENU_MISC_SHOW_SPLASH,           MENUITEM_BOOL,          &conf.splash_show },
    {0x5c,LANG_MENU_MISC_START_SOUND,           MENUITEM_BOOL,          &conf.start_sound },
#if CAM_ADJUSTABLE_ALT_BUTTON
    {0x22,LANG_MENU_MISC_ALT_BUTTON,            MENUITEM_ENUM,          (int*)gui_alt_mode_button_enum },
#endif
    {0x5d,LANG_MENU_MISC_DISABLE_LCD_OFF,       MENUITEM_ENUM,          (int*)gui_alt_power_enum },
    {0x65,LANG_MENU_MISC_PALETTE,               MENUITEM_PROC,          (int*)gui_draw_palette },
    {0x80,LANG_MENU_MISC_BUILD_INFO,            MENUITEM_PROC,          (int*)gui_show_build_info },
    {0x80,LANG_MENU_MISC_MEMORY_INFO,           MENUITEM_PROC,          (int*)gui_show_memory_info },
    {0x33,LANG_MENU_DEBUG_MAKE_BOOTABLE,        MENUITEM_PROC,          (int*)gui_menuproc_mkbootdisk },
    {0x2b,LANG_MENU_MAIN_RESET_OPTIONS,         MENUITEM_PROC,          (int*)gui_menuproc_reset },
    {0x51,LANG_MENU_BACK,                       MENUITEM_UP },
    {0},
};
static CMenu misc_submenu = {0x29,LANG_MENU_MISC_TITLE, NULL, misc_submenu_items };

static int voltage_step;
static CMenuItem battery_submenu_items[] = {
    {0x66,LANG_MENU_BATT_VOLT_MAX,              MENUITEM_INT|MENUITEM_ARG_ADDR_INC,     &conf.batt_volts_max,   (int)&voltage_step },
    {0x67,LANG_MENU_BATT_VOLT_MIN,              MENUITEM_INT|MENUITEM_ARG_ADDR_INC,     &conf.batt_volts_min,   (int)&voltage_step },
    {0x68,LANG_MENU_BATT_STEP_25,               MENUITEM_BOOL|MENUITEM_ARG_CALLBACK,    &conf.batt_step_25,     (int)cb_step_25 },
    {0x0,(int)"",                               MENUITEM_SEPARATOR },
    {0x73,LANG_MENU_BATT_SHOW_PERCENT,          MENUITEM_BOOL|MENUITEM_ARG_CALLBACK,    &conf.batt_perc_show },
    {0x32,LANG_MENU_BATT_SHOW_ICON,             MENUITEM_BOOL,                          &conf.batt_icon_show },
    {0x51,LANG_MENU_BACK,                       MENUITEM_UP },
    {0}
};
static CMenu battery_submenu = {0x32,LANG_MENU_BATT_TITLE, cb_battery_menu_change, battery_submenu_items };

static CMenuItem clock_submenu_items[] = {
    {0x6d,LANG_MENU_OSD_CLOCK_FORMAT,           MENUITEM_ENUM,          (int*)gui_clock_format_enum },
    {0x6c,LANG_MENU_OSD_CLOCK_INDICATOR,        MENUITEM_ENUM,          (int*)gui_clock_indicator_enum },
    {0x51,LANG_MENU_BACK,                       MENUITEM_UP },
    {0}
};
static CMenu clock_submenu = {0x34,LANG_MENU_OSD_CLOCK_PARAMS_TITLE, NULL, clock_submenu_items };





static CMenuItem operation_submenu_items[] = {
    {0x5c,LANG_MENU_MISC_FAST_EV,               MENUITEM_BOOL,          &conf.fast_ev },
    {0x5f,LANG_MENU_MISC_FAST_EV_STEP,          MENUITEM_ENUM,          (int*)gui_fast_ev_step },
    {0x51,LANG_MENU_BACK,                       MENUITEM_UP },
    {0}
};
static CMenu operation_submenu = {0x21,LANG_MENU_OPERATION_PARAM_TITLE, NULL, operation_submenu_items };



static CMenuItem visual_submenu_items[] = {
    {0x35,LANG_MENU_VIS_LANG,                   MENUITEM_PROC,          (int*)gui_draw_load_lang },
    {0x5f,LANG_MENU_VIS_OSD_FONT,               MENUITEM_ENUM,          (int*)gui_font_enum },
    {0x35,LANG_MENU_VIS_MENU_FONT,              MENUITEM_PROC,          (int*)gui_draw_load_menu_rbf },
    {0x35,LANG_MENU_VIS_MENU_SYMBOL_FONT,       MENUITEM_PROC,          (int*)gui_draw_load_symbol_rbf },
    {0x80,LANG_MENU_RESET_FILES,                MENUITEM_PROC,          (int*)gui_menuproc_reset_files },
    {0x0,LANG_MENU_VIS_COLORS,                  MENUITEM_SEPARATOR },
    {0x65,LANG_MENU_VIS_OSD_TEXT,               MENUITEM_COLOR_FG,      (int*)&conf.osd_color },
    {0x65,LANG_MENU_VIS_OSD_BKG,                MENUITEM_COLOR_BG,      (int*)&conf.osd_color },
    {0x65,LANG_MENU_VIS_OSD_WARNING,            MENUITEM_COLOR_FG,      (int*)&conf.osd_color_warn },
    {0x65,LANG_MENU_VIS_OSD_WARNING_BKG,        MENUITEM_COLOR_BG,      (int*)&conf.osd_color_warn },
    {0x65,LANG_MENU_VIS_HISTO,                  MENUITEM_COLOR_FG,      (int*)&conf.histo_color },
    {0x65,LANG_MENU_VIS_HISTO_BKG,              MENUITEM_COLOR_BG,      (int*)&conf.histo_color },
    {0x65,LANG_MENU_VIS_HISTO_BORDER,           MENUITEM_COLOR_FG,      (int*)&conf.histo_color2 },
    {0x65,LANG_MENU_VIS_HISTO_MARKERS,          MENUITEM_COLOR_BG,      (int*)&conf.histo_color2 },
    //{0x65,LANG_MENU_VIS_BATT_ICON,              MENUITEM_COLOR_FG,      (int*)&conf.batt_icon_color },
    {0x65,LANG_MENU_VIS_MENU_TEXT,              MENUITEM_COLOR_FG,      (int*)&conf.menu_color },
    {0x65,LANG_MENU_VIS_MENU_BKG,               MENUITEM_COLOR_BG,      (int*)&conf.menu_color },
    {0x65,LANG_MENU_VIS_MENU_TITLE_TEXT,        MENUITEM_COLOR_FG,      (int*)&conf.menu_title_color }, 
    {0x65,LANG_MENU_VIS_MENU_TITLE_BKG,         MENUITEM_COLOR_BG,      (int*)&conf.menu_title_color },
    {0x65,LANG_MENU_VIS_MENU_CURSOR_TEXT,       MENUITEM_COLOR_FG,      (int*)&conf.menu_cursor_color }, 
    {0x65,LANG_MENU_VIS_MENU_CURSOR_BKG,        MENUITEM_COLOR_BG,      (int*)&conf.menu_cursor_color },
    {0x65,LANG_MENU_VIS_MENU_SYMBOL_TEXT,       MENUITEM_COLOR_FG,      (int*)&conf.menu_symbol_color }, 
    {0x65,LANG_MENU_VIS_MENU_SYMBOL_BKG,        MENUITEM_COLOR_BG,      (int*)&conf.menu_symbol_color },
    {0x51,LANG_MENU_BACK,                       MENUITEM_UP },
    {0}
};
static CMenu visual_submenu = {0x28,LANG_MENU_VIS_TITLE, NULL, visual_submenu_items };


static CMenuItem osd_submenu_items[] = {
    //{0x5c,LANG_MENU_OSD_HIDE_PLAYBACK,          MENUITEM_ENUM,          (int*)gui_hide_osd_enum },
    //{0x81,LANG_MENU_VIS_MENU_CENTER,            MENUITEM_BOOL,          &conf.menu_center },
    //{0x81,LANG_MENU_SELECT_FIRST_ENTRY,         MENUITEM_BOOL,          &conf.menu_select_first_entry },       
    //{0x64,LANG_MENU_VIS_SYMBOL,                 MENUITEM_BOOL,          &conf.menu_symbol_enable },    

    {0x72,LANG_MENU_OSD_LAYOUT_EDITOR,          MENUITEM_PROC,          (int*)gui_draw_osd_le },
    {0x32,LANG_MENU_OSD_BATT_PARAMS,            MENUITEM_SUBMENU,       (int*)&battery_submenu },
    {0x34,LANG_MENU_OSD_CLOCK_PARAMS,           MENUITEM_SUBMENU,       (int*)&clock_submenu },
    {0x51,LANG_MENU_BACK,                       MENUITEM_UP },
    {0}
};

static CMenu osd_submenu = {0x22,LANG_MENU_OSD_TITLE, NULL, osd_submenu_items };

static CMenuItem histo_submenu_items[] = {
    {0x5f,LANG_MENU_HISTO_SHOW,                 MENUITEM_ENUM,          (int*)gui_histo_show_enum },
    {0x6f,LANG_MENU_HISTO_LAYOUT,               MENUITEM_ENUM,          (int*)gui_histo_layout_enum },
    {0x5f,LANG_MENU_HISTO_MODE,                 MENUITEM_ENUM,          (int*)gui_histo_mode_enum },
    {0x5c,LANG_MENU_HISTO_EXP,                  MENUITEM_BOOL,          &conf.show_overexp },
    {0x70,LANG_MENU_HISTO_IGNORE_PEAKS,         MENUITEM_INT|MENUITEM_F_UNSIGNED|MENUITEM_F_MINMAX,  &conf.histo_ignore_boundary,  MENU_MINMAX(0, 32) },
    {0x5c,LANG_MENU_HISTO_SHOW_EV_GRID,         MENUITEM_BOOL,          &conf.histo_show_ev_grid },
    {0x51,LANG_MENU_BACK,                       MENUITEM_UP },
    {0}
};
static CMenu histo_submenu = {0x25,LANG_MENU_HISTO_TITLE, NULL, histo_submenu_items };




static CMenuItem root_menu_items[] = {
    {0x21,LANG_MENU_OPERATION_PARAM,            MENUITEM_SUBMENU,       (int*)&operation_submenu },

    {0x25,LANG_MENU_MAIN_HISTO_PARAM,           MENUITEM_SUBMENU,       (int*)&histo_submenu },
    {0x22,LANG_MENU_MAIN_OSD_PARAM,             MENUITEM_SUBMENU,       (int*)&osd_submenu },
    {0x28,LANG_MENU_MAIN_VISUAL_PARAM,          MENUITEM_SUBMENU,       (int*)&visual_submenu },

    {0x29,LANG_MENU_MAIN_MISC,                  MENUITEM_SUBMENU,       (int*)&misc_submenu },
    {0}
};

static CMenu root_menu = {0x20,LANG_MENU_MAIN_TITLE, NULL, root_menu_items };

static char buf[256];

void rinit(){
    gui_menu_init(&root_menu);
}



//-------------------------------------------------------------------
void cb_step_25() {
    voltage_step = (conf.batt_step_25)?25:1;
}

void cb_battery_menu_change(unsigned int item) {
    switch (item) {
        case 0: //Voltage MAX
            if (conf.batt_volts_max<conf.batt_volts_min+25) {
                conf.batt_volts_min = conf.batt_volts_max-25;
            }
            break;
        case 1: //Voltage MIN
            if (conf.batt_volts_min>conf.batt_volts_max-25) {
                conf.batt_volts_max = conf.batt_volts_min+25;
            }
            break;
        default:
            break;
    }
}


/*
common code for "enum" menu items that just take a list of string values and don't require any special setters
would be better to have another menu item type that does this by default
save memory by eliminating dupe code
*/
static void gui_enum_value_change(int *value, int change, unsigned num_items) {
    *value+=change;
    if (*value<0)
        *value = num_items-1;
    else if (*value>=num_items)
        *value = 0;
}

static const char* gui_change_simple_enum(int* value, int change, const char** items, unsigned num_items) {
    gui_enum_value_change(value, change, num_items);
    return items[*value];
}


//-------------------------------------------------------------------
const char* gui_histo_mode_enum(int change, int arg) {
    static const char* modes[]={ "Linear", "Log" };

    gui_enum_value_change(&conf.histo_mode,change,sizeof(modes)/sizeof(modes[0]));

    histogram_set_mode(conf.histo_mode);

    return modes[conf.histo_mode];
}

//-------------------------------------------------------------------
const char* gui_histo_layout_enum(int change, int arg) {
    static const char* modes[]={ "RGB", "Y", "RGB Y",  "R G B", "RGB all", "Y all", "Blend", "Blend Y"};

    gui_enum_value_change(&conf.histo_layout,change,sizeof(modes)/sizeof(modes[0]));

    if (conf.histo_layout==OSD_HISTO_LAYOUT_Y || conf.histo_layout==OSD_HISTO_LAYOUT_Y_argb) {
        histogram_set_main(HISTO_Y);
    } else {
        histogram_set_main(HISTO_RGB);
    }

    return modes[conf.histo_layout];
}

//-------------------------------------------------------------------
const char* gui_font_enum(int change, int arg) {
    static const char* fonts[]={ "Win1250", "Win1251", "Win1252", "Win1253", "Win1254", "Win1257"};

    gui_enum_value_change(&conf.font_cp,change,sizeof(fonts)/sizeof(fonts[0]));

    if (change != 0) {
        font_set(conf.font_cp);
        if (!rbf_load(conf.menu_rbf_file))
            rbf_load_from_8x16(current_font);
        rbf_set_codepage(FONT_CP_WIN);
        gui_menu_init(NULL);
    }

    return fonts[conf.font_cp];
}




const char* gui_histo_show_enum(int change, int arg) {
    static const char* modes[]={ "Don't", "Always", "Shoot" };

    return gui_change_simple_enum(&conf.show_histo,change,modes,sizeof(modes)/sizeof(modes[0]));
}

const char* gui_hide_osd_enum(int change, int arg) {
    static const char* modes[]={ "Don't", "Playback", "Key DISP.", "both"};

    return gui_change_simple_enum(&conf.hide_osd,change,modes,sizeof(modes)/sizeof(modes[0]));
}

const char* gui_clock_format_enum(int change, int arg) {
    static const char* modes[]={ "24h", "12h"};

    return gui_change_simple_enum(&conf.clock_format,change,modes,sizeof(modes)/sizeof(modes[0]));
}

const char* gui_clock_indicator_enum(int change, int arg) {
    static const char* modes[]={ "PM", "P","."};

    return gui_change_simple_enum(&conf.clock_indicator,change,modes,sizeof(modes)/sizeof(modes[0]));
}

//-------------------------------------------------------------------
#if CAM_ADJUSTABLE_ALT_BUTTON
    const char* gui_alt_mode_button_enum(int change, int arg) {
    #if defined(CAMERA_s2is) || defined(CAMERA_s3is) || defined(CAMERA_s5is)
        static const char* names[]={ "Shrtcut", "Flash", "Timer", "ISO", "Video" };
        static const int keys[]={ KEY_PRINT, KEY_FLASH, KEY_TIMER, KEY_ISO, KEY_VIDEO };
    #elif defined(CAMERA_g7)
        static const char* names[]={ "Print", "FE"};
        static const int keys[]={ KEY_PRINT, KEY_MICROPHONE };
    #elif defined(CAMERA_g9)
        static const char* names[]={ "Print", "FE"};
        static const int keys[]={ KEY_PRINT,  KEY_MICROPHONE };
    #elif defined(CAMERA_a650)
        static const char* names[]={ "Print", "ISO"};
        static const int keys[]={ KEY_PRINT, KEY_ISO };
    #elif defined(CAMERA_sx100is) || defined(CAMERA_sx110is)
        static const char* names[]={ "Print", "Face"};
        static const int keys[]={ KEY_PRINT, KEY_FACE };
    #elif defined(CAMERA_sx10) || defined(CAMERA_sx1) || defined(CAMERA_sx20) || defined(CAMERA_sx30)
        static const char* names[]={ "Shrtcut", "Flash", "Video"};
        static const int keys[]={ KEY_PRINT, KEY_FLASH, KEY_VIDEO };
    #elif defined(CAMERA_a570) || defined(CAMERA_a590) || defined(CAMERA_a720)
        static const char* names[]={ "Print", "Display"};
        static const int keys[] = {KEY_PRINT, KEY_DISPLAY};
    #else
        #error camera alt-buttons not defined
    #endif
        int i;

        for (i=0; i<sizeof(names)/sizeof(names[0]); ++i) {
            if (conf.alt_mode_button==keys[i]) {
                break;
            }
        }

        i+=change;
        if (i<0)
            i=(sizeof(names)/sizeof(names[0]))-1;
        else if (i>=(sizeof(names)/sizeof(names[0])))
            i=0;

        conf.alt_mode_button = keys[i];
        kbd_set_alt_mode_key_mask(conf.alt_mode_button);
        return names[i];
    }
#endif

//-------------------------------------------------------------------
const char* gui_alt_power_enum(int change, int arg) {
// Script option is retained even if scripting is disabled, otherwise conf values will change
// Equivalent to ALT
    static const char* modes[]={ "Never", "Alt", "Script", "Always" };

    gui_enum_value_change(&conf.alt_prevent_shutdown,change,sizeof(modes)/sizeof(modes[0]));
        
    return modes[conf.alt_prevent_shutdown];
}
const char* gui_fast_ev_step(int change, int arg) {
    static const char* modes[]={"1/6 Ev","1/3 Ev","1/2 Ev", "2/3 Ev","5/6 Ev","1 Ev","1 1/6 Ev","1 1/3 Ev","1 1/2 Ev", "1 2/3 Ev","1 5/6 Ev","2 Ev","2 1/6 Ev","2 1/3 Ev","2 1/2 Ev", "2 2/3 Ev","2 5/6 Ev","3 Ev","3 1/6 Ev","3 1/3 Ev","3 1/2 Ev", "3 2/3 Ev","3 5/6 Ev","4 Ev"};
    
    return gui_change_simple_enum(&conf.fast_ev_step,change,modes,sizeof(modes)/sizeof(modes[0]));
}


    
//-------------------------------------------------------------------
static void gui_menuproc_reset_files(int arg){
    conf.lang_file[0] = 0;
    conf.menu_symbol_rbf_file[0] = 0;
    conf.menu_rbf_file[0] = 0;
    conf_save();
    gui_mbox_init(LANG_INFORMATION, LANG_MENU_RESTART_CAMERA, MBOX_BTN_OK|MBOX_TEXT_CENTER, NULL);
}

//-------------------------------------------------------------------
static volatile enum Gui_Mode gui_mode;
static volatile int gui_restore;
static volatile int gui_in_redraw;
static int gui_splash, gui_splash_mode;
static Conf old_conf;

//-------------------------------------------------------------------
void gui_init()
{

    gui_mode = GUI_MODE_NONE;
    gui_restore = 0;
    gui_in_redraw = 0;
    if (conf.start_sound>0)
    {
        play_sound(4);
    }
    gui_splash = (conf.splash_show)?SPLASH_TIME:0;
    gui_lang_init();
    draw_init();

    exposition_thresh = screen_size/500;
    voltage_step = (conf.batt_step_25)?25:1;
}

//-------------------------------------------------------------------
enum Gui_Mode gui_get_mode() {
    return gui_mode;
}

//-------------------------------------------------------------------
void gui_set_mode(enum Gui_Mode mode) {
    gui_mode = mode;
}

//-------------------------------------------------------------------
void gui_force_restore() {
    gui_restore = gui_in_redraw;
}

static void gui_handle_splash(void) {
    static char *logo = NULL;
    static int logo_size;
    if (gui_splash) {
        static int need_logo=1; // don't use logo ptr, since we don't want to keep re-trying
        if(need_logo) {
            const char *logo_name="A/CHDK/DATA/logo_lm.dat";
            FILE *fd;
            struct stat st;
            need_logo=0;
            if (stat(logo_name,&st) == 0) {
                logo_size=st.st_size;
                logo=malloc(logo_size);
                if(logo) {
                    fd = fopen(logo_name, "rb");
                    if(fd){
                        fread(logo,1,logo_size,fd);
                        fclose(fd);
                    }
                    else {
                        free(logo);
                        logo=NULL;
                        need_logo=1;
                    }
                }
            }
        }
        if (gui_splash>(SPLASH_TIME-4)) {
            gui_draw_splash(logo,logo_size);
        } else if (gui_splash==1 && (mode_get()&MODE_MASK) == gui_splash_mode && (gui_mode==GUI_MODE_NONE || gui_mode==GUI_MODE_ALT)) {
            draw_restore();
        }
        --gui_splash;
        if(!gui_splash) {
            free(logo);
            logo=NULL;
            need_logo=1;
        }
    }
}

//-------------------------------------------------------------------
void gui_redraw()
{
    enum Gui_Mode gui_mode_old;
    static int show_md_grid=0;

    gui_handle_splash();

    gui_in_redraw = 1;
    gui_mode_old = gui_mode;
    
#if CAM_USES_ASPECT_CORRECTION //nandoide sept-2009 
    //the different modes arises because games are designed on a 360x240 basis and are difficult to downscale to 320x240
    if (gui_mode == GUI_MODE_REVERSI || gui_mode == GUI_MODE_SOKOBAN || gui_mode == GUI_MODE_4WINS || gui_mode == GUI_MODE_MASTERMIND ||gui_mode == GUI_MODE_TETRIS) {
        draw_set_environment(aspect_xcorrection_games_360, aspect_ycorrection_games_360, GAMES_SCREEN_WIDTH, GAMES_SCREEN_HEIGHT);
    } else { //default
        //draw_set_environment(NULL, NULL, SCREENX, SCREENY);
        draw_set_environment(NULL, NULL, vid_get_bitmap_screen_width(), vid_get_bitmap_screen_height());
    }
#endif   
    switch (gui_mode) {
        case GUI_MODE_MENU:
            gui_menu_draw();
            //draw_txt_string(20, 14, "<CNF>", MAKE_COLOR(COLOR_ALT_BG, COLOR_FG));
            break;
        case GUI_MODE_ALT:
            gui_draw_osd();
            
            if ((mode_get()&MODE_MASK) == MODE_REC)
            {
                short mmode;
                get_property_case(PROPCASE_METERING_MODE, &mmode, sizeof(mmode));
                draw_txt_string(7, 14, mmode==0?"<E>":mmode==1?"<S>":"<C>", conf.osd_color);
            }
            else
                draw_txt_string(7, 14, "<->", conf.osd_color);

            console_draw();
            break;
        case GUI_MODE_NONE:
            //gui_draw_osd();
            break;
        case GUI_MODE_PALETTE:
            gui_palette_draw();
            break;
        case GUI_MODE_MBOX:
            gui_mbox_draw();
            break;
        case GUI_MODE_FSELECT:
            gui_fselect_draw();
            break;
        case GUI_MODE_OSD:
            gui_osd_draw();
            //draw_txt_string(20, 14, "<OSD>", MAKE_COLOR(COLOR_ALT_BG, COLOR_FG));
            break;

        case GUI_MODE_MPOPUP:
            gui_mpopup_draw();
            break;
        default:
            break;
    }

    gui_in_redraw = 0;
    if ((gui_mode_old != gui_mode && (gui_mode_old != GUI_MODE_NONE && gui_mode_old != GUI_MODE_ALT) && (gui_mode != GUI_MODE_MBOX && gui_mode != GUI_MODE_MPOPUP)) || gui_restore) {
        gui_restore = 0;

    }
}

    //-------------------------------------------------------------------
    static inline void conf_store_old_settings() {
        old_conf=conf;
    }

    //-------------------------------------------------------------------
    static inline int conf_save_new_settings_if_changed() {
        if (memcmp(&old_conf, &conf, sizeof(Conf)) != 0) {
            conf_save();
            conf_store_old_settings();
            return 1;
        }
        return 0;
    }

//-------------------------------------------------------------------
void gui_kbd_process()
{
    int clicked_key;

    if (kbd_is_key_clicked(KEY_MENU))
    {
        switch (gui_mode)
        {
            case GUI_MODE_ALT:
                gui_menu_init(&root_menu);
                gui_mode = GUI_MODE_MENU;
                draw_restore();
                break;
            case GUI_MODE_MENU:
                conf_save_new_settings_if_changed();
                gui_mode = GUI_MODE_ALT;
                draw_restore();
                break;
            case GUI_MODE_PALETTE:
            case GUI_MODE_OSD:

            case GUI_MODE_BENCH:
                draw_restore();
                gui_mode = GUI_MODE_MENU;
                break;
            case GUI_MODE_FSELECT:
                gui_fselect_kbd_process();
                break;
            default:
                break;
        }
        return;
    }

    switch (gui_mode) {
        case GUI_MODE_ALT:
            break;
            
            
        case GUI_MODE_MENU:
            gui_menu_kbd_process();
            break;
        case GUI_MODE_PALETTE:
            gui_palette_kbd_process();
            break;
        case GUI_MODE_MBOX:
            gui_mbox_kbd_process();
            break;
            case GUI_MODE_FSELECT:
                gui_fselect_kbd_process();
                break;
            
        case GUI_MODE_OSD:
            gui_osd_kbd_process();
            break;

        case GUI_MODE_MPOPUP:
            gui_mpopup_kbd_process();
            break;
        default:
            break;
    }
}

//-------------------------------------------------------------------
void gui_kbd_enter()
{
    // XXX set custom palette
    conf_store_old_settings();
    gui_mode = GUI_MODE_ALT;
    
    conf_update_prevent_shutdown();
    
    vid_turn_off_updates();
}

//-------------------------------------------------------------------
void gui_kbd_leave()
{
    // XXX restore palette
    conf_save_new_settings_if_changed();

    draw_restore();
    if (gui_mode == GUI_MODE_READ && !rbf_load(conf.menu_rbf_file))
        rbf_load_from_8x16(current_font);
    rbf_set_codepage(FONT_CP_WIN);
    vid_turn_on_updates();
    gui_mode = GUI_MODE_NONE;
    
    conf_update_prevent_shutdown();
}

//------------------------------------------------------------------- 
void other_kbd_process()
{ 
}


void gui_draw_osd() {
    unsigned int m, mode_photo, mode_video;
#if CAM_SWIVEL_SCREEN
    static int flashlight = 0;
#endif
    static int pressed = 0;
    static int half_disp_press_old=0;
    int half_disp_press;
    int need_restore = 0;
    m = mode_get();

#if CAM_SWIVEL_SCREEN
    if (conf.flashlight && (m&MODE_SCREEN_OPENED) && (m&MODE_SCREEN_ROTATED) && (gui_mode==GUI_MODE_NONE /* || gui_mode==GUI_MODE_ALT */)) {
        draw_filled_rect(0, 0, screen_width-1, screen_height-1, MAKE_COLOR(COLOR_WHITE, COLOR_WHITE));
        flashlight = 1;
    }
    if (flashlight) {
        if ((!((m&MODE_SCREEN_OPENED) && (m&MODE_SCREEN_ROTATED))) || (gui_mode!=GUI_MODE_NONE /* && gui_mode!=GUI_MODE_ALT */)) {
            flashlight = 0;
            need_restore = 1;
        } else {
            return;
        }
    }
#endif
    

    // TODO some of the ifs below should probably use this
    mode_video = MODE_IS_VIDEO(m);

    mode_photo = (m&MODE_MASK) == MODE_PLAY || 
                !( mode_video ||
                (m&MODE_SHOOTING_MASK)==MODE_STITCH);

    half_disp_press=mode_photo && kbd_is_key_pressed(KEY_SHOOT_HALF) && kbd_is_key_pressed(KEY_DISPLAY);
    if (half_disp_press && ! half_disp_press_old) 
        need_restore = 1;
    half_disp_press_old=half_disp_press;

    if (need_restore)
        draw_restore();

    if (half_disp_press) 
        return;

#if !CAM_SHOW_OSD_IN_SHOOT_MENU
      if (!((canon_menu_active==(int)&canon_menu_active-4) && (canon_shoot_menu_active==0)))  return;    
#else
      if (!((canon_menu_active==(int)&canon_menu_active-4) /*&& (canon_shoot_menu_active==0)*/ ))  return;
#endif  

    
    if ((gui_mode==GUI_MODE_NONE || gui_mode==GUI_MODE_ALT) &&
        (
            (kbd_is_key_pressed(KEY_SHOOT_HALF) && ((conf.show_histo==SHOW_HALF)/* || (m&MODE_MASK) == MODE_PLAY*/)) || 
            ((conf.show_histo==SHOW_ALWAYS)  &&   !((m&MODE_MASK) == MODE_PLAY) && (recreview_hold==0))
        ) && (mode_photo || (m&MODE_SHOOTING_MASK)==MODE_STITCH)) {
        gui_osd_draw_histo();
    }

    if ((m&MODE_MASK) == MODE_REC && recreview_hold==0 ) {
        if ((gui_mode==GUI_MODE_NONE || gui_mode==GUI_MODE_ALT) && (((kbd_is_key_pressed(KEY_SHOOT_HALF) || (state_kbd_script_run) || (shooting_get_common_focus_mode())) && (mode_photo || (m&MODE_SHOOTING_MASK)==MODE_STITCH )))) {
 
           gui_osd_calc_expo_param();           	           
        }
        
        gui_osd_draw_values(0);
    }


    if ((recreview_hold==0) &&  (!kbd_is_key_pressed(KEY_SHOOT_HALF) && (  ((m&MODE_MASK) == MODE_REC) || (!((m&MODE_MASK) == MODE_REC) &&  !((conf.hide_osd == 1) || (conf.hide_osd == 3)) )) && !(((conf.hide_osd == 2) || (conf.hide_osd == 3))&& (shooting_get_prop(PROPCASE_DISPLAY_MODE) == 1))))   {
        gui_batt_draw_osd();
    }
    
    if (0&&(recreview_hold==0) &&  ((!kbd_is_key_pressed(KEY_SHOOT_HALF) &&  (  ((m&MODE_MASK) == MODE_REC) || (!((m&MODE_MASK) == MODE_REC) &&  !((conf.hide_osd == 1) || (conf.hide_osd == 3)) )) && !(((conf.hide_osd == 2) || (conf.hide_osd == 3))&& (shooting_get_prop(PROPCASE_DISPLAY_MODE) == 1))) )) {
        gui_osd_draw_clock(0,0,0);
    }
}

//-------------------------------------------------------------------
static void gui_menuproc_reset_selected(unsigned int btn) {
    if (btn==MBOX_BTN_YES)
        conf_load_defaults();
}

void gui_menuproc_reset(int arg)
{
    gui_mbox_init(LANG_MSG_RESET_OPTIONS_TITLE, 
                  LANG_MSG_RESET_OPTIONS_TEXT,
                  MBOX_FUNC_RESTORE|MBOX_TEXT_CENTER|MBOX_BTN_YES_NO|MBOX_DEF_BTN2, gui_menuproc_reset_selected);
}

//-------------------------------------------------------------------
void gui_draw_palette(int arg) {
    draw_restore();
    gui_palette_init(PALETTE_MODE_DEFAULT, 0x00, NULL);
    gui_mode = GUI_MODE_PALETTE;
}

//-------------------------------------------------------------------
void gui_show_build_info(int arg) {
    static char comp[64];

#ifdef __GNUC__
# ifndef __GNUC_PATCHLEVEL__
# define __GNUC_PATCHLEVEL 0
# endif
    sprintf(comp, "GCC %d.%d.%d", __GNUC__ ,__GNUC_MINOR__,__GNUC_PATCHLEVEL__ );
#else
    sprintf(comp, "UNKNOWN" );
#endif
    sprintf(buf, lang_str(LANG_MSG_BUILD_INFO_TEXT), HDK_VERSION, BUILD_NUMBER, __DATE__, __TIME__, PLATFORM, PLATFORMSUB, comp);
    gui_mbox_init(LANG_MSG_BUILD_INFO_TITLE, (int)buf, MBOX_FUNC_RESTORE|MBOX_TEXT_LEFT, NULL);
}

//-------------------------------------------------------------------
void gui_show_memory_info(int arg) {
    sprintf(buf, lang_str(LANG_MSG_MEMORY_INFO_TEXT), core_get_free_memory(), MEMISOSIZE, &_start, &_end);
    gui_mbox_init(LANG_MSG_MEMORY_INFO_TITLE, (int)buf, MBOX_FUNC_RESTORE|MBOX_TEXT_CENTER, NULL);
}


#define LOGO_WIDTH  169
#define LOGO_HEIGHT 74

void gui_draw_splash(char* logo, int logo_size) {
    coord w, h, x, y;
    static const char *text[] = {
        "CHDK Version '" HDK_VERSION " " BUILD_NUMBER "'" , 
        "Build: " __DATE__ " " __TIME__ ,
        "Camera: " PLATFORM " - " PLATFORMSUB };
    int i, l;
    color cl = MAKE_COLOR(COLOR_RED, COLOR_WHITE);

    gui_splash_mode = (mode_get()&MODE_MASK);
    
    h=sizeof(text)/sizeof(text[0])*FONT_HEIGHT+8;
    w=0;
    for (i=0; i<sizeof(text)/sizeof(text[0]); ++i) {
        l=strlen(text[i]);
        if (l>w) w=l;
    }
    w=w*FONT_WIDTH+10;

    x = (screen_width-w)>>1; y = ((screen_height-h)>>1) + 20;
    draw_filled_round_rect(x, y, x+w, y+h, MAKE_COLOR(COLOR_RED, COLOR_RED));
    for (i=0; i<sizeof(text)/sizeof(text[0]); ++i) {
        draw_string(x+((w-strlen(text[i])*FONT_WIDTH)>>1), y+i*FONT_HEIGHT+4, text[i], cl);
    }
    if(logo){
      int pos;
      int mx=0;
      int my=0;
      int offset_x = (screen_width-LOGO_WIDTH)>>1;
      int offset_y = ((screen_height-LOGO_HEIGHT)>>1) - 42;
      const color color_lookup[8] = {COLOR_BLACK,
                                    COLOR_SPLASH_RED/*0x2E redish*/,
                                    COLOR_RED,
                                    COLOR_GREY /*0x3D*/,
                                    COLOR_SPLASH_GREY /*0x1F*/,
                                    COLOR_SPLASH_PINK /*0x21 pinkish*/,
                                    COLOR_TRANSPARENT /*0x00*/,
                                    COLOR_WHITE /*0x11*/};
      for(pos=0; pos<logo_size; pos++){
          char data = logo[pos];
          color c = color_lookup[(data>>5) & 0x07];
          for(i=0; i<(data&0x1F)+1; i++){
              if (c!=0x00){
                  draw_pixel(offset_x+mx,offset_y+my,c);
              }
              if (mx==LOGO_WIDTH){
                  mx=0;
                  my++;
              }else{
                  mx++;
              }     
          }
      }
    }
}

//-------------------------------------------------------------------
void gui_draw_osd_le(int arg) {
    gui_mode = GUI_MODE_OSD;
    gui_osd_init();
}


//-------------------------------------------------------------------
void gui_menuproc_mkbootdisk(int arg) {
    mark_filesystem_bootable();
}



//-------------------------------------------------------------------
static void gui_draw_menu_rbf_selected(const char *fn) {
    if (fn) {
        strcpy(conf.menu_rbf_file, fn);
        if (!rbf_load(conf.menu_rbf_file))
            rbf_load_from_8x16(current_font);
        rbf_set_codepage(FONT_CP_WIN);
        gui_menu_init(NULL);
    }
}
void gui_draw_load_menu_rbf(int arg) {
    DIR   *d;
    char  *path="A/CHDK/FONTS";

    // if exists "A/CHDK/FONTS" go into
    d=opendir(path);
    if (d) {
        closedir(d);
    } else {
        path="A";
    }

    gui_fselect_init(LANG_STR_SELECT_FONT_FILE, path, gui_draw_menu_rbf_selected);
}

//-------------------------------------------------------------------
static void gui_draw_symbol_rbf_selected(const char *fn) {
    if (fn) {
        strcpy(conf.menu_symbol_rbf_file, fn);
        if(!rbf_load_symbol(conf.menu_symbol_rbf_file)) conf.menu_symbol_enable=0;		//AKA
        gui_menu_init(NULL);
    }
}
void gui_draw_load_symbol_rbf(int arg) {
    DIR   *d;
    char  *path="A/CHDK/SYMBOLS";

    // if exists "A/CHDK/FONTS" go into
    d=opendir(path);
    if (d) {
        closedir(d);
    } else {
        path="A";
    }

    gui_fselect_init(LANG_STR_SELECT_SYMBOL_FILE, path, gui_draw_symbol_rbf_selected);
}

//-------------------------------------------------------------------
static void gui_draw_lang_selected(const char *fn) {
    if (fn) {
        strcpy(conf.lang_file, fn);
        lang_load_from_file(conf.lang_file);
        gui_menu_init(NULL);
    }
}
void gui_draw_load_lang(int arg) {
    DIR   *d;
    char  *path="A/CHDK/LANG";

    // if exists "A/CHDK/LANG" go into
    d=opendir(path);
    if (d) {
        closedir(d);
    } else {
        path="A";
    }

    gui_fselect_init(LANG_STR_SELECT_LANG_FILE, path, gui_draw_lang_selected);
}
